#include "x264encoder.h"
#include <x264.h>
#include <cstdio>
#include <memory>
#include <cstring>
#include <ostream>
#include <iostream>

#define kVideoBitrateMin 80000
#define kVideoBitrateMax 6000000

X264Encoder::X264Encoder()
{
	encoder_ = nullptr;
	encode_param_ = nullptr;
	frame_ = nullptr;
	encoded_frame_ = nullptr;
	is_open_ = false;
	encoded_frame_count_ = 0;
	enable_fixed_gop_ = true;
	gop_size_ = 400;
	timestamp_ = 0;
	force_keyframe_ = false;
}

X264Encoder::~X264Encoder()
{
	Close();
}

void X264Encoder::Close()
{
	if (encoder_) {
		x264_encoder_close(encoder_);
		encoder_ = NULL;
	}
	if (frame_)
	{
		x264_picture_clean(frame_);
		delete frame_;
		frame_ = nullptr;
	}
	if (encoded_frame_)
	{
		delete encoded_frame_;
		encoded_frame_ = nullptr;
	}
	if (encode_param_)
	{
		delete encode_param_;
		encode_param_ = nullptr;
	}
	is_open_ = false;
	encoded_frame_count_ = 0;
}

bool X264Encoder::Open(const encSetting& param)
{

	Close();

	encode_param_ = new x264_param_t;
	frame_ = new x264_picture_t;
	encoded_frame_ = new x264_picture_t;

	setting_ = param;

	x264_picture_init(encoded_frame_);
	x264_picture_alloc(frame_, X264_CSP_NV12, setting_.width, setting_.height);

	x264_param_default(encode_param_);
	x264_param_default_preset(encode_param_, "veryfast", "zerolatency");//会修改profile
	x264_param_apply_profile(encode_param_, "baseline");//baseline+cabac
	//x264_param_apply_profile_(encode_param_, "main");//老版本1.4的openh264不支持

	// encode_param_->i_level_idc = 40;
	// encode_param_->i_log_level = X264_LOG_NONE;
	encode_param_->i_csp = X264_CSP_NV12;
	encode_param_->i_bitdepth = 8;
	encode_param_->i_width = setting_.width;
	encode_param_->i_height = setting_.height;
	// encode_param_->b_repeat_headers = 1;  // 重复SPS/PPS 放到关键帧前面          
	// encode_param_->b_cabac = 1;
	// encode_param_->i_threads = 1;//多帧编码
	// encode_param_->b_sliced_threads = 0;//(webrtc ? 0 : 1);
	encode_param_->i_fps_num = setting_.fps;
	encode_param_->i_fps_den = 1;
	// encode_param_->i_keyint_max = setting_.fps;
	// if (enable_fixed_gop_)
	// {
	// 	encode_param_->i_keyint_max = gop_size_;
	// 	encode_param_->i_keyint_min = gop_size_;
	// }
	// encode_param_->i_frame_reference = 1;//参考帧

	// encode_param_->analyse.b_psnr = 1;

	//码率控制模式有ABR（平均码率）、CQP（恒定质量）、CRF（恒定码率）
	//	ABR模式下调整i_bitrate，vbv开了后就是CBR模式
	//	CQP下调整i_qp_constant调整QP值，范围0~51，值越大图像越模糊，默认23
	//	太细致了人眼也分辨不出来，为了增加编码速度降低数据量还是设大些好,
	//	CRF下调整f_rf_constant和f_rf_constant_max影响编码速度和图像质量（数据量）
	// encode_param_->rc.i_rc_method = X264_RC_ABR;
	//encode_param_->rc.i_qp_constant = 40;
	//encode_param_->rc.i_qp_min = 10;
	//encode_param_->rc.i_qp_max = 45;
	//encode_param_->rc.f_rf_constant = 20;
	//encode_param_->rc.f_rf_constant_max = 45;
	//encode_param_->rc.f_rate_tolerance = 0.1;
	// encode_param_->rc.i_bitrate = (int)setting_.bitrate_bps ;
	// encode_param_->rc.i_vbv_max_bitrate = (int)(setting_.bitrate_bps); // 平均码率模式下，最大瞬时码率，默认0(与-B设置相同)
	// encode_param_->rc.i_vbv_buffer_size = (int)(setting_.bitrate_bps * 3);
	// encode_param_->rc.f_ip_factor = 1.12; //调整I、P帧比例，使得码率控制更满足网络传输需要
	// encode_param_->rc.f_rate_tolerance = 0.75; //使码率控制更准确

	if ((encoder_ = x264_encoder_open(encode_param_)) == NULL)
	{
		Close();
		return false;
	}

	is_open_ = true;

	return true;
}

void X264Encoder::ReConfigure(const encSetting& param) {
	if (!is_open_) {
		return;
	}

	// 分辨率改变，重启编码器
	if (param.width != setting_.width || param.height != setting_.height) {
		Close();
		Open(param);
		return;
	}

	// 如果帧率码率都相同，那么不用处理，直接返回
	if (encode_param_->rc.i_bitrate == param.bitrate_bps &&
		encode_param_->i_fps_num == param.fps) {
		return;
	}

	// 码率设置
	if (encode_param_->rc.i_bitrate != param.bitrate_bps) {
		setting_.bitrate_bps = param.bitrate_bps;

		encode_param_->rc.i_vbv_max_bitrate = (int)(param.bitrate_bps); // 平均码率模式下，最大瞬时码率，默认0(与-B设置相同)
		encode_param_->rc.i_bitrate = (int)param.bitrate_bps;
		encode_param_->rc.i_vbv_buffer_size = (int)(param.bitrate_bps * 3);
	}

	// 帧率设置
	if (encode_param_->i_fps_num != param.fps) {
		encode_param_->i_fps_num = param.fps;
		encode_param_->i_fps_den = 1;
		encode_param_->i_keyint_max = param.fps;
		if (enable_fixed_gop_)
		{
			encode_param_->i_keyint_max = gop_size_;
			encode_param_->i_keyint_min = gop_size_;
		}
	}

	// TODO:QP
	
	int err = x264_encoder_reconfig(encoder_, encode_param_);

	if (err != 0)
	{
		printf("x264_encoder_reconfig failed! code %d\n", err);
	}

	setting_ = param;
}


bool X264Encoder::Encode( 
	const uint8_t** yuv_data, 
	uint8_t* pkt, 
	size_t& pkt_size, 
	bool& is_keyframe, 
	bool& got_output )
{
	is_keyframe = false;
	got_output = false;
	pkt_size = 0;

	if (!is_open_) {
		return false;
	}

	encoded_frame_count_++;

	int y_size = setting_.width * setting_.height;
	memcpy(frame_->img.plane[0] , yuv_data[0], y_size);
	memcpy(frame_->img.plane[1] , yuv_data[1], y_size / 2);


	frame_->i_pts = timestamp_++;

	if (force_keyframe_)
	{
		frame_->i_type = X264_TYPE_IDR;
		force_keyframe_ = false;
	}
	else
	{
		frame_->i_type = X264_TYPE_AUTO;
	}

	int iFrameSize = 0;
	int iNal = 0;
	x264_nal_t* pNals = NULL;

	//编码  
	int frame_size = x264_encoder_encode(encoder_, &pNals, &iNal, frame_, encoded_frame_);
	
	is_keyframe = IS_X264_TYPE_I(encoded_frame_->i_type) ? true : false;

	if (frame_size > 0 && iNal > 0)
	{
		for (int i = 0; i < iNal; ++i)
		{
			//int32_t i_num_nal_h = 0;

			// if (pNals[i].i_payload > 4)
			// {
			// 	while (i_num_nal_h < 5 && pNals[i].p_payload[i_num_nal_h] == 0)
			// 	{
			// 		i_num_nal_h++;
			// 	}
				
			// 	static char nal_head_s[3] = { 0, 0, 0 };

			// 	if (i_num_nal_h < 3)
			// 	{
			// 		memcpy(pkt + iFrameSize, nal_head_s, 3 - i_num_nal_h);
			// 		iFrameSize += (3 - i_num_nal_h);
			// 	}
			// }
			
			memcpy(pkt + iFrameSize, pNals[i].p_payload, pNals[i].i_payload);

			iFrameSize += pNals[i].i_payload;
		}

		got_output = true;

		pkt_size = iFrameSize;
	}
	else 
	{
		printf("x264 encode fail\n");
	}

	return true;
}

bool X264Encoder::Encode(
	const uint8_t* yuv_data,
	uint8_t* pkt,
	size_t& pkt_size,
	bool& is_keyframe,
	bool& got_output
) 
{
	if (!is_open_) {
		return false;
	}

	const uint8_t* yuv[3] = { 0 };
	if (yuv_data == NULL) {
		return false;
	}
	else {
		int y_size = setting_.width * setting_.height;
		yuv[0] = yuv_data;
		yuv[1] = yuv_data + y_size;
		yuv[2] = yuv_data + y_size * 5 / 4;
		return Encode(yuv, pkt, pkt_size, is_keyframe, got_output);
	}
}

//强制i帧
void X264Encoder::ForceKeyframe()
{
	force_keyframe_ = true;
}

VideoEncoder::encSetting X264Encoder::GetConfig() {
	return setting_;
}
